/*
 * 将 Datums 转换为 Python 对象，反之亦然
 *
 * src/pl/plpython/plpy_typeio.c
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_typeio.h"
#include "plpython.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"

/* 从 Datums 到 Python 对象的转换 */
static PyObject *fc_PLyBool_FromBool(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyFloat_FromFloat4(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyFloat_FromFloat8(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyDecimal_FromNumeric(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyLong_FromInt16(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyLong_FromInt32(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyLong_FromInt64(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyLong_FromOid(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyBytes_FromBytea(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyUnicode_FromScalar(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyObject_FromTransform(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyList_FromArray(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyList_FromArray_recurse(PLyDatumToOb *fc_elm, int *fc_dims, int fc_ndim, int fc_dim,
										   char **fc_dataptr_p, bits8 **fc_bitmap_p, int *fc_bitmask_p);
static PyObject *fc_PLyDict_FromComposite(PLyDatumToOb *fc_arg, Datum fc_d);
static PyObject *fc_PLyDict_FromTuple(PLyDatumToOb *fc_arg, HeapTuple fc_tuple, TupleDesc fc_desc, bool fc_include_generated);

/* 从 Python 对象到 Datums 的转换 */
static Datum fc_PLyObject_ToBool(PLyObToDatum *fc_arg, PyObject *fc_plrv,
							  bool *fc_isnull, bool fc_inarray);
static Datum fc_PLyObject_ToBytea(PLyObToDatum *fc_arg, PyObject *fc_plrv,
							   bool *fc_isnull, bool fc_inarray);
static Datum fc_PLyObject_ToComposite(PLyObToDatum *fc_arg, PyObject *fc_plrv,
								   bool *fc_isnull, bool fc_inarray);
static Datum fc_PLyObject_ToScalar(PLyObToDatum *fc_arg, PyObject *fc_plrv,
								bool *fc_isnull, bool fc_inarray);
static Datum fc_PLyObject_ToDomain(PLyObToDatum *fc_arg, PyObject *fc_plrv,
								bool *fc_isnull, bool fc_inarray);
static Datum fc_PLyObject_ToTransform(PLyObToDatum *fc_arg, PyObject *fc_plrv,
								   bool *fc_isnull, bool fc_inarray);
static Datum fc_PLySequence_ToArray(PLyObToDatum *fc_arg, PyObject *fc_plrv,
								 bool *fc_isnull, bool fc_inarray);
static void fc_PLySequence_ToArray_recurse(PyObject *fc_obj,
										ArrayBuildState **fc_astatep,
										int *fc_ndims, int *fc_dims, int fc_cur_depth,
										PLyObToDatum *fc_elm, Oid fc_elmbasetype);

/* 从 Python 对象到复合 Datums 的转换 */
static Datum fc_PLyUnicode_ToComposite(PLyObToDatum *fc_arg, PyObject *fc_string, bool fc_inarray);
static Datum fc_PLyMapping_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_mapping);
static Datum fc_PLySequence_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_sequence);
static Datum fc_PLyGenericObject_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_object, bool fc_inarray);


/*
 * 转换函数。 请记住，Python 的输出是 PostgreSQL 的输入，反之亦然。
 */

/*
 * 执行输入转换，给定正确设置的状态信息。
 *
 * 这是任何输入转换的外层入口点。 在内部，转换函数直接递归到彼此之间。
 */
PyObject * PLy_input_convert(PLyDatumToOb *fc_arg, Datum fc_val)
{
	PyObject   *fc_result;
	PLyExecutionContext *fc_exec_ctx = PLy_current_execution_context();
	MemoryContext fc_scratch_context = PLy_get_scratch_context(fc_exec_ctx);
	MemoryContext fc_oldcontext;

	/*
	 * 在临时上下文中执行工作，以避免从数据类型输出函数调用中泄漏内存。
	 * （单个 PLyDatumToObFunc 函数不能重置临时上下文，因为它们递归，
	 * 内部函数可能会覆盖外部函数仍然需要的数据。因此，我们在最外层递归级别执行一次。）
	 *
	 * 我们在每个转换周期之前而不是之后重置临时上下文。
	 * 这样，我们就不必释放结果对象的 Python 引用计数，以防 MemoryContextReset 抛出错误。
	 */
	MemoryContextReset(fc_scratch_context);

	fc_oldcontext = MemoryContextSwitchTo(fc_scratch_context);

	fc_result = fc_arg->func(fc_arg, fc_val);

	MemoryContextSwitchTo(fc_oldcontext);

	return fc_result;
}

/*
 * 执行输出转换，给定正确设置的状态信息。
 *
 * 这是任何输出转换的外层入口点。 在内部，转换函数直接递归到彼此之间。
 *
 * 结果以及在此过程中生成的任何杂物都在当前内存上下文中。 调用者负责清理。
 */
Datum PLy_output_convert(PLyObToDatum *fc_arg, PyObject *fc_val, bool *fc_isnull)
{
	/* 在外层，我们不考虑数组元素 */
	return fc_arg->func(fc_arg, fc_val, fc_isnull, false);
}

/*
 * 将元组转换为 Python 字典对象。
 *
 * 注意：tupdesc 必须与用于设置 *arg 的元组描述符匹配。我们可以
 * 强制此函数从 *arg 中查找元组描述符，
 * 但实际上所有调用者都有正确的元组描述符可用。
 */
PyObject * PLy_input_from_tuple(PLyDatumToOb *fc_arg, HeapTuple fc_tuple, TupleDesc fc_desc, bool fc_include_generated)
{
	PyObject   *fc_dict;
	PLyExecutionContext *fc_exec_ctx = PLy_current_execution_context();
	MemoryContext fc_scratch_context = PLy_get_scratch_context(fc_exec_ctx);
	MemoryContext fc_oldcontext;

	/*
	 * 如同 PLy_input_convert，在临时上下文中执行工作。
	 */
	MemoryContextReset(fc_scratch_context);

	fc_oldcontext = MemoryContextSwitchTo(fc_scratch_context);

	fc_dict = fc_PLyDict_FromTuple(fc_arg, fc_tuple, fc_desc, fc_include_generated);

	MemoryContextSwitchTo(fc_oldcontext);

	return fc_dict;
}

/*
 * 初始化或重新初始化复合类型的每列输入信息。
 *
 * 这与 PLy_input_setup_func() 是分开的，因为在涉及匿名记录类型的情况下，
 * 我们需要显式传递 tupdesc。 在这种情况下，调用者有责任确保 tupdesc
 * 具有足够的生命周期。如果 tupdesc 是命名复合类型或注册记录类型，则
 * 不需要长时间存在。
 */
void PLy_input_setup_tuple(PLyDatumToOb *fc_arg, TupleDesc fc_desc, PLyProcedure *fc_proc)
{
	int			fc_i;

	/* 我们应该在之前设置好的结构上工作 */
	Assert(fc_arg->func == fc_PLyDict_FromComposite);

	/* 保存对 tupdesc 的指针，但仅在这是匿名记录类型时 */
	if (fc_arg->typoid == RECORDOID && fc_arg->typmod < 0)
		fc_arg->u.tuple.recdesc = fc_desc;

	/* 按需（重新）分配 atts 数组 */
	if (fc_arg->u.tuple.natts != fc_desc->natts)
	{
		if (fc_arg->u.tuple.atts)
			pfree(fc_arg->u.tuple.atts);
		fc_arg->u.tuple.natts = fc_desc->natts;
		fc_arg->u.tuple.atts = (PLyDatumToOb *)
			MemoryContextAllocZero(fc_arg->mcxt,
								   fc_desc->natts * sizeof(PLyDatumToOb));
	}

	/* 填充 atts 项目，除了被丢弃的列 */
	for (fc_i = 0; fc_i < fc_desc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_desc, fc_i);
		PLyDatumToOb *fc_att = &fc_arg->u.tuple.atts[fc_i];

		if (fc_attr->attisdropped)
			continue;

		if (fc_att->typoid == fc_attr->atttypid && fc_att->typmod == fc_attr->atttypmod)
			continue;			/* 已经设置好这个条目 */

		PLy_input_setup_func(fc_att, fc_arg->mcxt,
							 fc_attr->atttypid, fc_attr->atttypmod,
							 fc_proc);
	}
}

/*
 * 初始化或重新初始化复合类型的每列输出信息。
 *
 * 这与 PLy_output_setup_func() 是分开的，因为在涉及
 * 匿名记录类型的情况下，我们需要显式传递 tupdesc。
 * 在这种情况下，调用者有责任确保 tupdesc 的生命周期足够长。
 * 如果 tupdesc 是命名复合类型或注册记录类型，则不需要长生命周期。
 */
void PLy_output_setup_tuple(PLyObToDatum *fc_arg, TupleDesc fc_desc, PLyProcedure *fc_proc)
{
	int			fc_i;

	/* 我们应该在之前设置好的结构上工作 */
	Assert(fc_arg->func == fc_PLyObject_ToComposite);

	/* 保存对 tupdesc 的指针，但仅在这是匿名记录类型时 */
	if (fc_arg->typoid == RECORDOID && fc_arg->typmod < 0)
		fc_arg->u.tuple.recdesc = fc_desc;

	/* 按需（重新）分配 atts 数组 */
	if (fc_arg->u.tuple.natts != fc_desc->natts)
	{
		if (fc_arg->u.tuple.atts)
			pfree(fc_arg->u.tuple.atts);
		fc_arg->u.tuple.natts = fc_desc->natts;
		fc_arg->u.tuple.atts = (PLyObToDatum *)
			MemoryContextAllocZero(fc_arg->mcxt,
								   fc_desc->natts * sizeof(PLyObToDatum));
	}

	/* 填充 atts 项目，除了被丢弃的列 */
	for (fc_i = 0; fc_i < fc_desc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_desc, fc_i);
		PLyObToDatum *fc_att = &fc_arg->u.tuple.atts[fc_i];

		if (fc_attr->attisdropped)
			continue;

		if (fc_att->typoid == fc_attr->atttypid && fc_att->typmod == fc_attr->atttypmod)
			continue;			/* 已经设置好这个条目 */

		PLy_output_setup_func(fc_att, fc_arg->mcxt,
							  fc_attr->atttypid, fc_attr->atttypmod,
							  fc_proc);
	}
}

/*
 * 为返回记录的 PL/Python 函数设置输出信息。
 *
 * 注意：给定的 tupdesc 不一定是长生命周期的。
 */
void PLy_output_setup_record(PLyObToDatum *fc_arg, TupleDesc fc_desc, PLyProcedure *fc_proc)
{
	/* 除非是 RECORD，否则没有意义 */
	Assert(fc_arg->typoid == RECORDOID);
	Assert(fc_desc->tdtypeid == RECORDOID);

	/*
	 * 如果尚未完成，给记录类型加上标记。我们反正必须这么做
	 * 来返回一个元组，因此我们不妨强制执行该问题，以便使用
	 * 已知记录类型的代码路径。
	 */
	BlessTupleDesc(fc_desc);

	/*
	 * 更新 arg->typmod，并在其更改时清除 recdesc 链接。
	 * PLyObject_ToComposite 的下一次调用将查找记录类型的长生命周期 tupdesc。
	 */
	fc_arg->typmod = fc_desc->tdtypmod;
	if (fc_arg->u.tuple.recdesc &&
		fc_arg->u.tuple.recdesc->tdtypmod != fc_arg->typmod)
		fc_arg->u.tuple.recdesc = NULL;

	/* 如有必要，更新派生数据 */
	PLy_output_setup_tuple(fc_arg, fc_desc, fc_proc);
}

/*
 * 递归初始化 PLyObToDatum 结构，以便根据指定的 typeOid/typmod 从 Python 值构造 SQL 值。
 * （但请注意，此时我们可能有 RECORDOID/-1，即不确定的记录类型。）
 * proc 用于查找转换函数。
 */
void PLy_output_setup_func(PLyObToDatum *fc_arg, MemoryContext fc_arg_mcxt,
					  Oid fc_typeOid, int32 fc_typmod,
					  PLyProcedure *fc_proc)
{
	TypeCacheEntry *fc_typentry;
	char		fc_typtype;
	Oid			fc_trfuncid;
	Oid			fc_typinput;

	/* 因为这是递归的，理论上可能会导致溢出 */
	check_stack_depth();

	fc_arg->typoid = fc_typeOid;
	fc_arg->typmod = fc_typmod;
	fc_arg->mcxt = fc_arg_mcxt;

	/*
	 * 获取目标类型的 typcache 条目，请求我们需要的任何信息。
	 * RECORD 是一种特例：只需将其视为复合类型，而不去烦扰 typcache 条目。
	 */
	if (fc_typeOid != RECORDOID)
	{
		fc_typentry = lookup_type_cache(fc_typeOid, TYPECACHE_DOMAIN_BASE_INFO);
		fc_typtype = fc_typentry->typtype;
		fc_arg->typbyval = fc_typentry->typbyval;
		fc_arg->typlen = fc_typentry->typlen;
		fc_arg->typalign = fc_typentry->typalign;
	}
	else
	{
		fc_typentry = NULL;
		fc_typtype = TYPTYPE_COMPOSITE;
		/* 关于类型 RECORD 的硬编码知识： */
		fc_arg->typbyval = false;
		fc_arg->typlen = -1;
		fc_arg->typalign = TYPALIGN_DOUBLE;
	}

	/*
	 * 选择转换方法。请注意，转换函数仅针对复合和标量类型进行检查，
	 * 而不针对数组或域。这在历史上是这样，但如果在域上允许它们，我们将遇到问题，
	 * 因为我们通过所有级别的域嵌套深入而不查看中间级别。
	 */
	if (fc_typtype == TYPTYPE_DOMAIN)
	{
		/* 域 */
		fc_arg->func = fc_PLyObject_ToDomain;
		fc_arg->u.domain.domain_info = NULL;
		/* 递归设置元素类型的转换信息 */
		fc_arg->u.domain.base = (PLyObToDatum *)
			MemoryContextAllocZero(fc_arg_mcxt, sizeof(PLyObToDatum));
		PLy_output_setup_func(fc_arg->u.domain.base, fc_arg_mcxt,
							  fc_typentry->domainBaseType,
							  fc_typentry->domainBaseTypmod,
							  fc_proc);
	}
	else if (fc_typentry &&
			 IsTrueArrayType(fc_typentry))
	{
		/* 标准数组 */
		fc_arg->func = fc_PLySequence_ToArray;
		/* 获取基本类型 OID 插入到构造数组中 */
		/* （注意这可能与直接子类型不同） */
		fc_arg->u.array.elmbasetype = getBaseType(fc_typentry->typelem);
		/* 递归设置元素类型的转换信息 */
		fc_arg->u.array.elm = (PLyObToDatum *)
			MemoryContextAllocZero(fc_arg_mcxt, sizeof(PLyObToDatum));
		PLy_output_setup_func(fc_arg->u.array.elm, fc_arg_mcxt,
							  fc_typentry->typelem, fc_typmod,
							  fc_proc);
	}
	else if ((fc_trfuncid = get_transform_tosql(fc_typeOid,
											 fc_proc->langid,
											 fc_proc->trftypes)))
	{
		fc_arg->func = fc_PLyObject_ToTransform;
		fmgr_info_cxt(fc_trfuncid, &fc_arg->u.transform.typtransform, fc_arg_mcxt);
	}
	else if (fc_typtype == TYPTYPE_COMPOSITE)
	{
		/* 命名复合类型或 RECORD */
		fc_arg->func = fc_PLyObject_ToComposite;
		/* 我们稍后将设置每个字段的数据 */
		fc_arg->u.tuple.recdesc = NULL;
		fc_arg->u.tuple.typentry = fc_typentry;
		fc_arg->u.tuple.tupdescid = INVALID_TUPLEDESC_IDENTIFIER;
		fc_arg->u.tuple.atts = NULL;
		fc_arg->u.tuple.natts = 0;
		/* 在需要之前将其标记为无效 */
		fc_arg->u.tuple.recinfunc.fn_oid = InvalidOid;
	}
	else
	{
		/* 标量类型，但我们有几个特例 */
		switch (fc_typeOid)
		{
			case BOOLOID:
				fc_arg->func = fc_PLyObject_ToBool;
				break;
			case BYTEAOID:
				fc_arg->func = fc_PLyObject_ToBytea;
				break;
			default:
				fc_arg->func = fc_PLyObject_ToScalar;
				getTypeInputInfo(fc_typeOid, &fc_typinput, &fc_arg->u.scalar.typioparam);
				fmgr_info_cxt(fc_typinput, &fc_arg->u.scalar.typfunc, fc_arg_mcxt);
				break;
		}
	}
}

/*
 * 递归初始化 PLyDatumToOb 结构以构造指定类型Oid/typmod 的 SQL 值到 Python 值。
 * （但请注意，此时我们可能有 RECORDOID/-1，即，不确定的记录类型。）
 * proc 用于查找转换函数。
 */
void PLy_input_setup_func(PLyDatumToOb *fc_arg, MemoryContext fc_arg_mcxt,
					 Oid fc_typeOid, int32 fc_typmod,
					 PLyProcedure *fc_proc)
{
	TypeCacheEntry *fc_typentry;
	char		fc_typtype;
	Oid			fc_trfuncid;
	Oid			fc_typoutput;
	bool		fc_typisvarlena;

	/* 因为这是递归的，理论上可能会导致溢出 */
	check_stack_depth();

	fc_arg->typoid = fc_typeOid;
	fc_arg->typmod = fc_typmod;
	fc_arg->mcxt = fc_arg_mcxt;

	/*
	 * 获取目标类型的 typcache 条目，请求我们需要的任何信息。
	 * RECORD 是一种特例：只需将其视为复合类型，而不去烦扰 typcache 条目。
	 */
	if (fc_typeOid != RECORDOID)
	{
		fc_typentry = lookup_type_cache(fc_typeOid, TYPECACHE_DOMAIN_BASE_INFO);
		fc_typtype = fc_typentry->typtype;
		fc_arg->typbyval = fc_typentry->typbyval;
		fc_arg->typlen = fc_typentry->typlen;
		fc_arg->typalign = fc_typentry->typalign;
	}
	else
	{
		fc_typentry = NULL;
		fc_typtype = TYPTYPE_COMPOSITE;
		/* 关于类型 RECORD 的硬编码知识： */
		fc_arg->typbyval = false;
		fc_arg->typlen = -1;
		fc_arg->typalign = TYPALIGN_DOUBLE;
	}

	/*
	 * 选择转换方法。请注意，转换函数仅针对复合和标量类型进行检查，
	 * 而不针对数组或域。这在历史上是这样，但如果在域上允许它们，我们将遇到问题，
	 * 因为我们通过所有级别的域嵌套深入而不查看中间级别。
	 */
	if (fc_typtype == TYPTYPE_DOMAIN)
	{
		/* 域 --- 我们不在乎，只需递归到底层类型 */
		PLy_input_setup_func(fc_arg, fc_arg_mcxt,
							 fc_typentry->domainBaseType,
							 fc_typentry->domainBaseTypmod,
							 fc_proc);
	}
	else if (fc_typentry &&
			 IsTrueArrayType(fc_typentry))
	{
		/* 标准数组 */
		fc_arg->func = fc_PLyList_FromArray;
		/* 递归设置元素类型的转换信息 */
		fc_arg->u.array.elm = (PLyDatumToOb *)
			MemoryContextAllocZero(fc_arg_mcxt, sizeof(PLyDatumToOb));
		PLy_input_setup_func(fc_arg->u.array.elm, fc_arg_mcxt,
							 fc_typentry->typelem, fc_typmod,
							 fc_proc);
	}
	else if ((fc_trfuncid = get_transform_fromsql(fc_typeOid,
											   fc_proc->langid,
											   fc_proc->trftypes)))
	{
		fc_arg->func = fc_PLyObject_FromTransform;
		fmgr_info_cxt(fc_trfuncid, &fc_arg->u.transform.typtransform, fc_arg_mcxt);
	}
	else if (fc_typtype == TYPTYPE_COMPOSITE)
	{
		/* 命名复合类型或 RECORD */
		fc_arg->func = fc_PLyDict_FromComposite;
		/* 我们稍后将设置每个字段的数据 */
		fc_arg->u.tuple.recdesc = NULL;
		fc_arg->u.tuple.typentry = fc_typentry;
		fc_arg->u.tuple.tupdescid = INVALID_TUPLEDESC_IDENTIFIER;
		fc_arg->u.tuple.atts = NULL;
		fc_arg->u.tuple.natts = 0;
	}
	else
	{
		/* 标量类型，但我们有几个特例 */
		switch (fc_typeOid)
		{
			case BOOLOID:
				fc_arg->func = fc_PLyBool_FromBool;
				break;
			case FLOAT4OID:
				fc_arg->func = fc_PLyFloat_FromFloat4;
				break;
			case FLOAT8OID:
				fc_arg->func = fc_PLyFloat_FromFloat8;
				break;
			case NUMERICOID:
				fc_arg->func = fc_PLyDecimal_FromNumeric;
				break;
			case INT2OID:
				fc_arg->func = fc_PLyLong_FromInt16;
				break;
			case INT4OID:
				fc_arg->func = fc_PLyLong_FromInt32;
				break;
			case INT8OID:
				fc_arg->func = fc_PLyLong_FromInt64;
				break;
			case OIDOID:
				fc_arg->func = fc_PLyLong_FromOid;
				break;
			case BYTEAOID:
				fc_arg->func = fc_PLyBytes_FromBytea;
				break;
			default:
				fc_arg->func = fc_PLyUnicode_FromScalar;
				getTypeOutputInfo(fc_typeOid, &fc_typoutput, &fc_typisvarlena);
				fmgr_info_cxt(fc_typoutput, &fc_arg->u.scalar.typfunc, fc_arg_mcxt);
				break;
		}
	}
}


/*
 * 特殊用途的输入转换器。
 */

static PyObject * fc_PLyBool_FromBool(PLyDatumToOb *fc_arg, Datum fc_d)
{
	if (DatumGetBool(fc_d))
		Py_RETURN_TRUE;
	Py_RETURN_FALSE;
}

static PyObject * fc_PLyFloat_FromFloat4(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyFloat_FromDouble(DatumGetFloat4(fc_d));
}

static PyObject * fc_PLyFloat_FromFloat8(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyFloat_FromDouble(DatumGetFloat8(fc_d));
}

static PyObject * fc_PLyDecimal_FromNumeric(PLyDatumToOb *fc_arg, Datum fc_d)
{
	static PyObject *fc_decimal_constructor;
	char	   *fc_str;
	PyObject   *fc_pyvalue;

	/* 尝试导入 cdecimal。如果不存在，则回退到 decimal。 */
	if (!fc_decimal_constructor)
	{
		PyObject   *fc_decimal_module;

		fc_decimal_module = PyImport_ImportModule("cdecimal");
		if (!fc_decimal_module)
		{
			PyErr_Clear();
			fc_decimal_module = PyImport_ImportModule("decimal");
		}
		if (!fc_decimal_module)
			PLy_elog(ERROR, "could not import a module for Decimal constructor");

		fc_decimal_constructor = PyObject_GetAttrString(fc_decimal_module, "Decimal");
		if (!fc_decimal_constructor)
			PLy_elog(ERROR, "no Decimal attribute in module");
	}

	fc_str = DatumGetCString(DirectFunctionCall1(numeric_out, fc_d));
	fc_pyvalue = PyObject_CallFunction(fc_decimal_constructor, "s", fc_str);
	if (!fc_pyvalue)
		PLy_elog(ERROR, "conversion from numeric to Decimal failed");

	return fc_pyvalue;
}

static PyObject * fc_PLyLong_FromInt16(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyLong_FromLong(DatumGetInt16(fc_d));
}

static PyObject * fc_PLyLong_FromInt32(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyLong_FromLong(DatumGetInt32(fc_d));
}

static PyObject * fc_PLyLong_FromInt64(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyLong_FromLongLong(DatumGetInt64(fc_d));
}

static PyObject * fc_PLyLong_FromOid(PLyDatumToOb *fc_arg, Datum fc_d)
{
	return PyLong_FromUnsignedLong(DatumGetObjectId(fc_d));
}

static PyObject * fc_PLyBytes_FromBytea(PLyDatumToOb *fc_arg, Datum fc_d)
{
	text	   *fc_txt = DatumGetByteaPP(fc_d);
	char	   *fc_str = VARDATA_ANY(fc_txt);
	size_t		fc_size = VARSIZE_ANY_EXHDR(fc_txt);

	return PyBytes_FromStringAndSize(fc_str, fc_size);
}


/*
 * 使用 SQL 类型的输出函数进行通用输入转换。
 */
static PyObject * fc_PLyUnicode_FromScalar(PLyDatumToOb *fc_arg, Datum fc_d)
{
	char	   *fc_x = OutputFunctionCall(&fc_arg->u.scalar.typfunc, fc_d);
	PyObject   *fc_r = PLyUnicode_FromString(fc_x);

	pfree(fc_x);
	return fc_r;
}

/*
 * 使用 from-SQL 转换函数进行转换。
 */
static PyObject * fc_PLyObject_FromTransform(PLyDatumToOb *fc_arg, Datum fc_d)
{
	Datum		fc_t;

	fc_t = FunctionCall1(&fc_arg->u.transform.typtransform, fc_d);
	return (PyObject *) DatumGetPointer(fc_t);
}

/*
 * 将 SQL 数组转换为 Python 列表。
 */
static PyObject * fc_PLyList_FromArray(PLyDatumToOb *fc_arg, Datum fc_d)
{
	ArrayType  *fc_array = DatumGetArrayTypeP(fc_d);
	PLyDatumToOb *fc_elm = fc_arg->u.array.elm;
	int			fc_ndim;
	int		   *fc_dims;
	char	   *fc_dataptr;
	bits8	   *fc_bitmap;
	int			fc_bitmask;

	if (ARR_NDIM(fc_array) == 0)
		return PyList_New(0);

	/* 数组维度和左边界 */
	fc_ndim = ARR_NDIM(fc_array);
	fc_dims = ARR_DIMS(fc_array);
	Assert(fc_ndim <= MAXDIM);

	/*
	 * 我们以数据项中存储的物理顺序遍历 SQL 数组。
	 * 例如，对于三维数组，迭代的顺序如下：[0,0,0] 元素到 [0,0,k]，然后 [0,1,0]
	 * 到 [0,1,k] 直到 [0,m,k]，然后 [1,0,0] 到 [1,0,k] 直到
	 * [1,m,k]，依此类推。
	 *
	 * 在 Python 中，没有多维列表，但它们表示为列表的列表。
	 * 因此，一个 [n,m,k] 元素的三维数组是一个包含 n 个 m 元素数组的列表，
	 * 每个元素是一个 k 元素的数组。 
	 * PLyList_FromArray_recurse() 为单一维度构建 Python 列表，并对下一个内部维度进行递归。
	 */
	fc_dataptr = ARR_DATA_PTR(fc_array);
	fc_bitmap = ARR_NULLBITMAP(fc_array);
	fc_bitmask = 1;

	return fc_PLyList_FromArray_recurse(fc_elm, fc_dims, fc_ndim, 0,
									 &fc_dataptr, &fc_bitmap, &fc_bitmask);
}

static PyObject * fc_PLyList_FromArray_recurse(PLyDatumToOb *fc_elm, int *fc_dims, int fc_ndim, int fc_dim,
						  char **fc_dataptr_p, bits8 **fc_bitmap_p, int *fc_bitmask_p)
{
	int			fc_i;
	PyObject   *fc_list;

	fc_list = PyList_New(fc_dims[fc_dim]);
	if (!fc_list)
		return NULL;

	if (fc_dim < fc_ndim - 1)
	{
		/* 外维度。对每个内部切片进行递归。 */
		for (fc_i = 0; fc_i < fc_dims[fc_dim]; fc_i++)
		{
			PyObject   *fc_sublist;

			fc_sublist = fc_PLyList_FromArray_recurse(fc_elm, fc_dims, fc_ndim, fc_dim + 1,
												fc_dataptr_p, fc_bitmap_p, fc_bitmask_p);
			PyList_SET_ITEM(fc_list, fc_i, fc_sublist);
		}
	}
	else
	{
		/*
		 * 最内部的维度。使用此切片中的数组值填充列表。
		 */
		char	   *fc_dataptr = *fc_dataptr_p;
		bits8	   *fc_bitmap = *fc_bitmap_p;
		int			fc_bitmask = *fc_bitmask_p;

		for (fc_i = 0; fc_i < fc_dims[fc_dim]; fc_i++)
		{
			/* 检查 NULL */
			if (fc_bitmap && (*fc_bitmap & fc_bitmask) == 0)
			{
				Py_INCREF(Py_None);
				PyList_SET_ITEM(fc_list, fc_i, Py_None);
			}
			else
			{
				Datum		fc_itemvalue;

				fc_itemvalue = fetch_att(fc_dataptr, fc_elm->typbyval, fc_elm->typlen);
				PyList_SET_ITEM(fc_list, fc_i, fc_elm->func(fc_elm, fc_itemvalue));
				fc_dataptr = att_addlength_pointer(fc_dataptr, fc_elm->typlen, fc_dataptr);
				fc_dataptr = (char *) att_align_nominal(fc_dataptr, fc_elm->typalign);
			}

			/* 如果有，向前推进位图指针 */
			if (fc_bitmap)
			{
				fc_bitmask <<= 1;
				if (fc_bitmask == 0x100 /* (1<<8) */ )
				{
					fc_bitmap++;
					fc_bitmask = 1;
				}
			}
		}

		*fc_dataptr_p = fc_dataptr;
		*fc_bitmap_p = fc_bitmap;
		*fc_bitmask_p = fc_bitmask;
	}

	return fc_list;
}

/*
 * 将复合 SQL 值转换为 Python dict。
 */
static PyObject * fc_PLyDict_FromComposite(PLyDatumToOb *fc_arg, Datum fc_d)
{
	PyObject   *fc_dict;
	HeapTupleHeader fc_td;
	Oid			fc_tupType;
	int32		fc_tupTypmod;
	TupleDesc	fc_tupdesc;
	HeapTupleData fc_tmptup;

	fc_td = DatumGetHeapTupleHeader(fc_d);
	/* 提取行类型信息并找到 tupdesc */
	fc_tupType = HeapTupleHeaderGetTypeId(fc_td);
	fc_tupTypmod = HeapTupleHeaderGetTypMod(fc_td);
	fc_tupdesc = lookup_rowtype_tupdesc(fc_tupType, fc_tupTypmod);

	/* 如果尚未完成，则设置 I/O 函数 */
	PLy_input_setup_tuple(fc_arg, fc_tupdesc,
						  PLy_current_execution_context()->curr_proc);

	/* 构建一个临时 HeapTuple 控制结构 */
	fc_tmptup.t_len = HeapTupleHeaderGetDatumLength(fc_td);
	fc_tmptup.t_data = fc_td;

	fc_dict = fc_PLyDict_FromTuple(fc_arg, &fc_tmptup, fc_tupdesc, true);

	ReleaseTupleDesc(fc_tupdesc);

	return fc_dict;
}

/*
 * 将元组转换为 Python dict 对象。
 */
static PyObject * fc_PLyDict_FromTuple(PLyDatumToOb *fc_arg, HeapTuple fc_tuple, TupleDesc fc_desc, bool fc_include_generated)
{
	PyObject   *volatile fc_dict;

	/* 简单的有效性检查，确保 desc 匹配 */
	Assert(fc_desc->natts == fc_arg->u.tuple.natts);

	fc_dict = PyDict_New();
	if (fc_dict == NULL)
		return NULL;

	PG_TRY();
	{
		int			fc_i;

		for (fc_i = 0; fc_i < fc_arg->u.tuple.natts; fc_i++)
		{
			PLyDatumToOb *fc_att = &fc_arg->u.tuple.atts[fc_i];
			Form_pg_attribute fc_attr = TupleDescAttr(fc_desc, fc_i);
			char	   *fc_key;
			Datum		fc_vattr;
			bool		fc_is_null;
			PyObject   *fc_value;

			if (fc_attr->attisdropped)
				continue;

			if (fc_attr->attgenerated)
			{
				/* 除非请求，否则不包含 */
				if (!fc_include_generated)
					continue;
			}

			fc_key = NameStr(fc_attr->attname);
			fc_vattr = heap_getattr(fc_tuple, (fc_i + 1), fc_desc, &fc_is_null);

			if (fc_is_null)
				PyDict_SetItemString(fc_dict, fc_key, Py_None);
			else
			{
				fc_value = fc_att->func(fc_att, fc_vattr);
				PyDict_SetItemString(fc_dict, fc_key, fc_value);
				Py_DECREF(fc_value);
			}
		}
	}
	PG_CATCH();
	{
		Py_DECREF(fc_dict);
		PG_RE_THROW();
	}
	PG_END_TRY();

	return fc_dict;
}

/*
 * 将Python对象转换为PostgreSQL布尔数据。 这不能通过
 * 通用转换函数，因为Python将布尔值附加到所有事物中，超出了
 * PostgreSQL布尔类型可以解析的范围。
 */
static Datum fc_PLyObject_ToBool(PLyObToDatum *fc_arg, PyObject *fc_plrv,
				 bool *fc_isnull, bool fc_inarray)
{
	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;
	return BoolGetDatum(PyObject_IsTrue(fc_plrv));
}

/*
 * 将Python对象转换为PostgreSQL bytea数据。 这不通过
 * 通用转换函数以规避嵌入空值的问题。这样更快。
 */
static Datum fc_PLyObject_ToBytea(PLyObToDatum *fc_arg, PyObject *fc_plrv,
				  bool *fc_isnull, bool fc_inarray)
{
	PyObject   *volatile fc_plrv_so = NULL;
	Datum		fc_rv = (Datum) 0;

	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;

	fc_plrv_so = PyObject_Bytes(fc_plrv);
	if (!fc_plrv_so)
		PLy_elog(ERROR, "could not create bytes representation of Python object");

	PG_TRY();
	{
		char	   *fc_plrv_sc = PyBytes_AsString(fc_plrv_so);
		size_t		fc_len = PyBytes_Size(fc_plrv_so);
		size_t		fc_size = fc_len + VARHDRSZ;
		bytea	   *fc_result = palloc(fc_size);

		SET_VARSIZE(fc_result, fc_size);
		memcpy(VARDATA(fc_result), fc_plrv_sc, fc_len);
		fc_rv = PointerGetDatum(fc_result);
	}
	PG_FINALLY();
	{
		Py_XDECREF(fc_plrv_so);
	}
	PG_END_TRY();

	return fc_rv;
}


/*
 * 将Python对象转换为复合类型。 首先查找类型的
 * 描述，然后通过转换函数将Python对象路由
 * 以获取PostgreSQL元组。
 */
static Datum fc_PLyObject_ToComposite(PLyObToDatum *fc_arg, PyObject *fc_plrv,
					  bool *fc_isnull, bool fc_inarray)
{
	Datum		fc_rv;
	TupleDesc	fc_desc;

	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;

	/*
	 * 字符串转换案例不需要tupdesc，也不需要每个字段
	 * 转换数据，因此如果这就是要使用的情况，就直接进行。
	 */
	if (PyUnicode_Check(fc_plrv))
		return fc_PLyUnicode_ToComposite(fc_arg, fc_plrv, fc_inarray);

	/*
	 * 如果我们处理的是命名复合类型，则每次必须查找
	 * tupdesc，以防类型发生可能的变化。
	 * RECORD类型在调用之间不能更改；但是我们仍然必须愿意
	 * 第一时间设置信息，如果没人做过的话。
	 */
	if (fc_arg->typoid != RECORDOID)
	{
		fc_desc = lookup_rowtype_tupdesc(fc_arg->typoid, fc_arg->typmod);
		/* 我们应该拥有类型的typcache条目的描述符 */
		Assert(fc_desc == fc_arg->u.tuple.typentry->tupDesc);
		/* 检测描述符的变化，必要时更新缓存 */
		if (fc_arg->u.tuple.tupdescid != fc_arg->u.tuple.typentry->tupDesc_identifier)
		{
			PLy_output_setup_tuple(fc_arg, fc_desc,
								   PLy_current_execution_context()->curr_proc);
			fc_arg->u.tuple.tupdescid = fc_arg->u.tuple.typentry->tupDesc_identifier;
		}
	}
	else
	{
		fc_desc = fc_arg->u.tuple.recdesc;
		if (fc_desc == NULL)
		{
			fc_desc = lookup_rowtype_tupdesc(fc_arg->typoid, fc_arg->typmod);
			fc_arg->u.tuple.recdesc = fc_desc;
		}
		else
		{
			/* 固定描述符以匹配下面的解除固定 */
			PinTupleDesc(fc_desc);
		}
	}

	/* 对我们的缓存进行简单的完整性检查 */
	Assert(fc_desc->natts == fc_arg->u.tuple.natts);

	/*
	 * 根据提供的Python对象的类型，使用适当的方法进行转换。
	 */
	if (PySequence_Check(fc_plrv))
		/* 复合类型作为序列（元组、列表等） */
		fc_rv = fc_PLySequence_ToComposite(fc_arg, fc_desc, fc_plrv);
	else if (PyMapping_Check(fc_plrv))
		/* 复合类型作为映射（当前仅为dict） */
		fc_rv = fc_PLyMapping_ToComposite(fc_arg, fc_desc, fc_plrv);
	else
		/* 作为某物返回，必须提供方法__getattr__(name) */
		fc_rv = fc_PLyGenericObject_ToComposite(fc_arg, fc_desc, fc_plrv, fc_inarray);

	ReleaseTupleDesc(fc_desc);

	return fc_rv;
}


/*
 * 将Python对象转换为服务器编码的C字符串。
 *
 * 注意：这已导出用于附加转换模块。
 */
char * PLyObject_AsString(PyObject *fc_plrv)
{
	PyObject   *fc_plrv_bo;
	char	   *fc_plrv_sc;
	size_t		fc_plen;
	size_t		fc_slen;

	if (PyUnicode_Check(fc_plrv))
		fc_plrv_bo = PLyUnicode_Bytes(fc_plrv);
	else if (PyFloat_Check(fc_plrv))
	{
		/* 对浮点数使用repr()，str()会导致数据丢失 */
		PyObject   *fc_s = PyObject_Repr(fc_plrv);

		fc_plrv_bo = PLyUnicode_Bytes(fc_s);
		Py_XDECREF(fc_s);
	}
	else
	{
		PyObject   *fc_s = PyObject_Str(fc_plrv);

		fc_plrv_bo = PLyUnicode_Bytes(fc_s);
		Py_XDECREF(fc_s);
	}
	if (!fc_plrv_bo)
		PLy_elog(ERROR, "could not create string representation of Python object");

	fc_plrv_sc = pstrdup(PyBytes_AsString(fc_plrv_bo));
	fc_plen = PyBytes_Size(fc_plrv_bo);
	fc_slen = strlen(fc_plrv_sc);

	Py_XDECREF(fc_plrv_bo);

	if (fc_slen < fc_plen)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("could not convert Python object into cstring: Python string representation appears to contain null bytes")));
	else if (fc_slen > fc_plen)
		elog(ERROR, "could not convert Python object into cstring: Python string longer than reported length");
	pg_verifymbstr(fc_plrv_sc, fc_slen, false);

	return fc_plrv_sc;
}


/*
 * 通用输出转换函数：将PyObject转换为cstring，并
 * 将cstring转换为PostgreSQL类型。
 */
static Datum fc_PLyObject_ToScalar(PLyObToDatum *fc_arg, PyObject *fc_plrv,
				   bool *fc_isnull, bool fc_inarray)
{
	char	   *fc_str;

	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;

	fc_str = PLyObject_AsString(fc_plrv);

	return InputFunctionCall(&fc_arg->u.scalar.typfunc,
							 fc_str,
							 fc_arg->u.scalar.typioparam,
							 fc_arg->typmod);
}


/*
 * 转换为域类型。
 */
static Datum fc_PLyObject_ToDomain(PLyObToDatum *fc_arg, PyObject *fc_plrv,
				   bool *fc_isnull, bool fc_inarray)
{
	Datum		fc_result;
	PLyObToDatum *fc_base = fc_arg->u.domain.base;

	fc_result = fc_base->func(fc_base, fc_plrv, fc_isnull, fc_inarray);
	domain_check(fc_result, *fc_isnull, fc_arg->typoid,
				 &fc_arg->u.domain.domain_info, fc_arg->mcxt);
	return fc_result;
}


/*
 * 使用to-SQL转换函数进行转换。
 */
static Datum fc_PLyObject_ToTransform(PLyObToDatum *fc_arg, PyObject *fc_plrv,
					  bool *fc_isnull, bool fc_inarray)
{
	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;
	return FunctionCall1(&fc_arg->u.transform.typtransform, PointerGetDatum(fc_plrv));
}


/*
 * 将Python序列（或列表的列表）转换为SQL数组。
 */
static Datum fc_PLySequence_ToArray(PLyObToDatum *fc_arg, PyObject *fc_plrv,
					bool *fc_isnull, bool fc_inarray)
{
	ArrayBuildState *fc_astate = NULL;
	int			fc_ndims = 1;
	int			fc_dims[MAXDIM];
	int			fc_lbs[MAXDIM];

	if (fc_plrv == Py_None)
	{
		*fc_isnull = true;
		return (Datum) 0;
	}
	*fc_isnull = false;

	/*
	 * 出于历史原因，我们允许在将Python对象转换为SQL数组时，
	 * 顶层使用任何序列（不仅仅是列表）。 但是，仅在对象包含
	 * 真实列表时，才会识别多维数组。
	 */
	if (!PySequence_Check(fc_plrv))
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("return value of function with array return type is not a Python sequence")));

	/* 使用第一层维度初始化维度信息 */
	memset(fc_dims, 0, sizeof(fc_dims));
	fc_dims[0] = PySequence_Length(fc_plrv);

	/*
	 * 深度优先遍历Python列表，并将底层所有元素收集到
	 * ArrayBuildState中。
	 */
	fc_PLySequence_ToArray_recurse(fc_plrv, &fc_astate,
								&fc_ndims, fc_dims, 1,
								fc_arg->u.array.elm,
								fc_arg->u.array.elmbasetype);

	/* 确保在没有输入的情况下获得零维数组，符合 PG 约定 */
	if (fc_astate == NULL)
		return PointerGetDatum(construct_empty_array(fc_arg->u.array.elmbasetype));

	for (int fc_i = 0; fc_i < fc_ndims; fc_i++)
		fc_lbs[fc_i] = 1;

	return makeMdArrayResult(fc_astate, fc_ndims, fc_dims, fc_lbs,
							 CurrentMemoryContext, true);
}

/*
 * PLySequence_ToArray的辅助函数。 深度优先遍历Python的列表列表，
 * 将元素存储在*astatep中。
 *
 * 仅在我们首次找到标量元素时创建ArrayBuildState；
 * 如果我们不这样做，则需要其他约定来知道
 * 我们是否已经发现任何标量（因此，维数已被冻结）。
 */
static void fc_PLySequence_ToArray_recurse(PyObject *fc_obj, ArrayBuildState **fc_astatep,
							int *fc_ndims, int *fc_dims, int fc_cur_depth,
							PLyObToDatum *fc_elm, Oid fc_elmbasetype)
{
	int			fc_i;
	int			fc_len = PySequence_Length(fc_obj);

	/* 我们不应该在非序列对象上到达这里 */
	if (fc_len < 0)
		PLy_elog(ERROR, "could not determine sequence length for function return value");

	for (fc_i = 0; fc_i < fc_len; fc_i++)
	{
		/* 获取数组元素 */
		PyObject   *fc_subobj = PySequence_GetItem(fc_obj, fc_i);

		/* 需要 PG_TRY 以确保我们释放子对象的引用计数 */
		PG_TRY();
		{
			/* 多维数组？ */
			if (PyList_Check(fc_subobj))
			{
				/* 在此层的第一个元素设置大小，否则进行比较 */
				if (fc_i == 0 && *fc_ndims == fc_cur_depth)
				{
					/* 在同一层次中有一些标量之后的数组？ */
					if (*fc_astatep != NULL)
						ereport(ERROR,
								(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
								 errmsg("multidimensional arrays must have array expressions with matching dimensions")));
					/* 太多维度？ */
					if (fc_cur_depth >= MAXDIM)
						ereport(ERROR,
								(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
								 errmsg("number of array dimensions exceeds the maximum allowed (%d)",
										MAXDIM)));
					/* 好吧，添加一个维度 */
					fc_dims[*fc_ndims] = PySequence_Length(fc_subobj);
					(*fc_ndims)++;
				}
				else if (fc_cur_depth >= *fc_ndims ||
						 PySequence_Length(fc_subobj) != fc_dims[fc_cur_depth])
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("multidimensional arrays must have array expressions with matching dimensions")));

				/* 递归以获取此子数组的元素 */
				fc_PLySequence_ToArray_recurse(fc_subobj, fc_astatep,
											fc_ndims, fc_dims, fc_cur_depth + 1,
											fc_elm, fc_elmbasetype);
			}
			else
			{
				Datum		fc_dat;
				bool		fc_isnull;

				/* 在同一层级之后有标量吗？ */
				if (*fc_ndims != fc_cur_depth)
					ereport(ERROR,
							(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
							 errmsg("multidimensional arrays must have array expressions with matching dimensions")));

				/* 将非列表对象转换为 Datum */
				fc_dat = fc_elm->func(fc_elm, fc_subobj, &fc_isnull, true);

				/* 如果我们还没有创建 ArrayBuildState */
				if (*fc_astatep == NULL)
					*fc_astatep = initArrayResult(fc_elmbasetype,
											   CurrentMemoryContext, true);

				/* ... 并将元素值保存在其中 */
				(void) accumArrayResult(*fc_astatep, fc_dat, fc_isnull,
										fc_elmbasetype, CurrentMemoryContext);
			}
		}
		PG_FINALLY();
		{
			Py_XDECREF(fc_subobj);
		}
		PG_END_TRY();
	}
}


/*
 * 使用 record_in 将 Python 字符串转换为复合类型。
 */
static Datum fc_PLyUnicode_ToComposite(PLyObToDatum *fc_arg, PyObject *fc_string, bool fc_inarray)
{
	char	   *fc_str;

	/*
	 * 如果我们还没有，为 record_in 设置调用数据。 (我们不能只
	 * 使用 DirectFunctionCall，因为 record_in 需要一个 fn_extra 字段。)
	 */
	if (!OidIsValid(fc_arg->u.tuple.recinfunc.fn_oid))
		fmgr_info_cxt(F_RECORD_IN, &fc_arg->u.tuple.recinfunc, fc_arg->mcxt);

	fc_str = PLyObject_AsString(fc_string);

	/*
	 * 如果我们在数组中解析复合类型，而字符串
	 * 不是有效的记录字面量，那么函数很有可能执行了类似于：
	 *
	 * CREATE FUNCTION .. RETURNS comptype[] AS $$ return [['foo', 'bar']] $$
	 * LANGUAGE plpython;
	 *
	 * 在 PostgreSQL 10 之前，这被解释为一个一维数组，
	 * 包含记录 ('foo', 'bar')。 PostgreSQL 10 添加了对
	 * 多维数组的支持，现在它被解释为一个
	 * 二维数组，包含两个记录，'foo' 和 'bar'。
	 * record_in() 会抛出一个错误，因为 "foo" 不是有效的记录
	 * 字面量。
	 *
	 * 为了使升级自旧版本的用户不那么困惑，尽量在典型情况下提供提示。
	 * 如果我们在解析复合类型的数组时，看到一个不是有效记录字面量的字符串字面量，给出提示。
	 * 我们只想在格式不正确的字符串字面量的狭窄情况下给出提示，而不是来自 record_in() 的任何错误，
	 * 所以在这里特别检查这个情况。
	 *
	 * 这个检查最好与 record_in() 中的检查匹配，以免禁止
	 * 实际上有效的字面量！
	 */
	if (fc_inarray)
	{
		char	   *fc_ptr = fc_str;

		/* 允许前导空格 */
		while (*fc_ptr && isspace((unsigned char) *fc_ptr))
			fc_ptr++;
		if (*fc_ptr++ != '(')
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
					 errmsg("malformed record literal: \"%s\"", fc_str),
					 errdetail("Missing left parenthesis."),
					 errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".")));
	}

	return InputFunctionCall(&fc_arg->u.tuple.recinfunc,
							 fc_str,
							 fc_arg->typoid,
							 fc_arg->typmod);
}


static Datum fc_PLyMapping_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_mapping)
{
	Datum		fc_result;
	HeapTuple	fc_tuple;
	Datum	   *fc_values;
	bool	   *fc_nulls;
	volatile int fc_i;

	Assert(PyMapping_Check(fc_mapping));

	/* 构建元组 */
	fc_values = palloc(sizeof(Datum) * fc_desc->natts);
	fc_nulls = palloc(sizeof(bool) * fc_desc->natts);
	for (fc_i = 0; fc_i < fc_desc->natts; ++fc_i)
	{
		char	   *fc_key;
		PyObject   *volatile fc_value;
		PLyObToDatum *fc_att;
		Form_pg_attribute fc_attr = TupleDescAttr(fc_desc, fc_i);

		if (fc_attr->attisdropped)
		{
			fc_values[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
			continue;
		}

		fc_key = NameStr(fc_attr->attname);
		fc_value = NULL;
		fc_att = &fc_arg->u.tuple.atts[fc_i];
		PG_TRY();
		{
			fc_value = PyMapping_GetItemString(fc_mapping, fc_key);
			if (!fc_value)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("key \"%s\" not found in mapping", fc_key),
						 errhint("To return null in a column, "
								 "add the value None to the mapping with the key named after the column.")));

			fc_values[fc_i] = fc_att->func(fc_att, fc_value, &fc_nulls[fc_i], false);

			Py_XDECREF(fc_value);
			fc_value = NULL;
		}
		PG_CATCH();
		{
			Py_XDECREF(fc_value);
			PG_RE_THROW();
		}
		PG_END_TRY();
	}

	fc_tuple = heap_form_tuple(fc_desc, fc_values, fc_nulls);
	fc_result = heap_copy_tuple_as_datum(fc_tuple, fc_desc);
	heap_freetuple(fc_tuple);

	pfree(fc_values);
	pfree(fc_nulls);

	return fc_result;
}


static Datum fc_PLySequence_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_sequence)
{
	Datum		fc_result;
	HeapTuple	fc_tuple;
	Datum	   *fc_values;
	bool	   *fc_nulls;
	volatile int fc_idx;
	volatile int fc_i;

	Assert(PySequence_Check(fc_sequence));

	/*
	 * 检查序列长度是否与 PG 元组完全相同。我们实际上
	 * 可以忽略超出项或将缺失项视为 null，但为了避免
	 * plpython 开发者的错误，我们在这里是严格的
	 */
	fc_idx = 0;
	for (fc_i = 0; fc_i < fc_desc->natts; fc_i++)
	{
		if (!TupleDescAttr(fc_desc, fc_i)->attisdropped)
			fc_idx++;
	}
	if (PySequence_Length(fc_sequence) != fc_idx)
		ereport(ERROR,
				(errcode(ERRCODE_DATATYPE_MISMATCH),
				 errmsg("length of returned sequence did not match number of columns in row")));

	/* 构建元组 */
	fc_values = palloc(sizeof(Datum) * fc_desc->natts);
	fc_nulls = palloc(sizeof(bool) * fc_desc->natts);
	fc_idx = 0;
	for (fc_i = 0; fc_i < fc_desc->natts; ++fc_i)
	{
		PyObject   *volatile fc_value;
		PLyObToDatum *fc_att;

		if (TupleDescAttr(fc_desc, fc_i)->attisdropped)
		{
			fc_values[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
			continue;
		}

		fc_value = NULL;
		fc_att = &fc_arg->u.tuple.atts[fc_i];
		PG_TRY();
		{
			fc_value = PySequence_GetItem(fc_sequence, fc_idx);
			Assert(fc_value);

			fc_values[fc_i] = fc_att->func(fc_att, fc_value, &fc_nulls[fc_i], false);

			Py_XDECREF(fc_value);
			fc_value = NULL;
		}
		PG_CATCH();
		{
			Py_XDECREF(fc_value);
			PG_RE_THROW();
		}
		PG_END_TRY();

		fc_idx++;
	}

	fc_tuple = heap_form_tuple(fc_desc, fc_values, fc_nulls);
	fc_result = heap_copy_tuple_as_datum(fc_tuple, fc_desc);
	heap_freetuple(fc_tuple);

	pfree(fc_values);
	pfree(fc_nulls);

	return fc_result;
}


static Datum fc_PLyGenericObject_ToComposite(PLyObToDatum *fc_arg, TupleDesc fc_desc, PyObject *fc_object, bool fc_inarray)
{
	Datum		fc_result;
	HeapTuple	fc_tuple;
	Datum	   *fc_values;
	bool	   *fc_nulls;
	volatile int fc_i;

	/* 构建元组 */
	fc_values = palloc(sizeof(Datum) * fc_desc->natts);
	fc_nulls = palloc(sizeof(bool) * fc_desc->natts);
	for (fc_i = 0; fc_i < fc_desc->natts; ++fc_i)
	{
		char	   *fc_key;
		PyObject   *volatile fc_value;
		PLyObToDatum *fc_att;
		Form_pg_attribute fc_attr = TupleDescAttr(fc_desc, fc_i);

		if (fc_attr->attisdropped)
		{
			fc_values[fc_i] = (Datum) 0;
			fc_nulls[fc_i] = true;
			continue;
		}

		fc_key = NameStr(fc_attr->attname);
		fc_value = NULL;
		fc_att = &fc_arg->u.tuple.atts[fc_i];
		PG_TRY();
		{
			fc_value = PyObject_GetAttrString(fc_object, fc_key);
			if (!fc_value)
			{
				/*
				 * 在对象中没有此列的属性。
				 *
				 * 如果我们在数组中解析复合类型，可能的
				 * 原因是函数包含类似于 "[[123,
				 * 'foo']]" 的内容。在 PostgreSQL 10 之前，这被解释为一个
				 * 数组，里面有复合类型 (123, 'foo')。但现在
				 * 它被解释为一个二维数组，我们尝试将 "123" 解释为复合类型。
				 * 参见 PLyObject_ToScalar() 中的类似启发式实现。
				 */
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("attribute \"%s\" does not exist in Python object", fc_key),
						 fc_inarray ?
						 errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".") :
						 errhint("To return null in a column, let the returned object have an attribute named after column with value None.")));
			}

			fc_values[fc_i] = fc_att->func(fc_att, fc_value, &fc_nulls[fc_i], false);

			Py_XDECREF(fc_value);
			fc_value = NULL;
		}
		PG_CATCH();
		{
			Py_XDECREF(fc_value);
			PG_RE_THROW();
		}
		PG_END_TRY();
	}

	fc_tuple = heap_form_tuple(fc_desc, fc_values, fc_nulls);
	fc_result = heap_copy_tuple_as_datum(fc_tuple, fc_desc);
	heap_freetuple(fc_tuple);

	pfree(fc_values);
	pfree(fc_nulls);

	return fc_result;
}
